/**
 * Copyright (C) 2013 Daniel Gee.
 * See LICENSE.txt for more details.
 */
package com.rl.dungeon;

/**
 * Enum to represent the directions. X is the width, with positive to the right. Y is the height,
 * with positive downwards. Z is the depth, with positive for deeper levels.
 * 
 * @author Daniel Gee
 */
public enum Direction
{
	/**
	 * Downwards, (0,0,1) in coordinate space.
	 */
	Down(0, 0, 1),

	/**
	 * East, (1,0,0) in coordinate space.
	 */
	East(1, 0, 0),

	/**
	 * No direction at all. You can usually use this instead of <tt>null</tt>.
	 */
	None(0, 0, 0),

	/**
	 * North, (0,-1,0) in coordinate space.
	 */
	North(0, -1, 0),

	/**
	 * NorthEast, (1,-1,0) in coordinate space.
	 */
	NorthEast(1, -1, 0),

	/**
	 * NorthWest, (-1,-1,0) in coordinate space.
	 */
	NorthWest(-1, -1, 0),

	/**
	 * South, (0,1,0) in coordinate space.
	 */
	South(0, 1, 0),

	/**
	 * SouthEast, (1,1,0) in coordinate space.
	 */
	SouthEast(1, 1, 0),

	/**
	 * SouthWest, (-1,1,0) in coordinate space.
	 */
	SouthWest(-1, 1, 0),

	/**
	 * Upwards, (0,0,-1) in coordinate space.
	 */
	Up(0, 0, -1),

	/**
	 * West, (-1,0,0) in coordinate space.
	 */
	West(-1, 0, 0);

	/**
	 * The cardinal directions are North, East, South, and West.
	 * 
	 * @return an array containing the four cardinal directions.
	 */
	public static Direction[] cardinals()
	{
		return new Direction[]
		{ North, East, South, West };
	}

	/**
	 * A lateral direction is any of the eight directions that aren't Up, Down, or None.
	 * 
	 * @return an array containing the eight lateral directions.
	 */
	public static Direction[] laterals()
	{
		return new Direction[]
		{ North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest };
	}

	/**
	 * This direction's x change after 1 unit of movement.
	 */
	public final int dx;

	/**
	 * This direction's y change after 1 unit of movement.
	 */
	public final int dy;

	/**
	 * This direction's z change after 1 unit of movement.
	 */
	public final int dz;

	/**
	 * Constructs a new direction
	 * 
	 * @param dx
	 *            this direction's change in x
	 * @param dy
	 *            this direction's change in y
	 * @param dz
	 *            this direction's change in z
	 */
	private Direction(int dx, int dy, int dz)
	{
		this.dx = dx;
		this.dy = dy;
		this.dz = dz;
	}

	/**
	 * Returns a new location that's 1 step translated from the location specified in the direction
	 * of the instance that you call this on. For example, <tt>Direction.North.from(loc)</tt>
	 * 
	 * @param location
	 *            the location
	 * @return the location one step in the direction of the callie.
	 */
	public Location from(Location location)
	{
		return location.translate(this);
	}

	/**
	 * @return if this instance is a cardinal direction or not.
	 */
	public boolean isCardinal()
	{
		if (this == North || this == South || this == East || this == West)
		{
			return true;
		}
		return false;
	}

	/**
	 * @return if this intance is a lateral direction or not.
	 */
	public boolean isLateral()
	{
		if (this == Up || this == Down || this == None)
		{
			return false;
		}
		return true;
	}

	/**
	 * @return the direction that is the reverse of this direction.
	 */
	public Direction reverse()
	{
		switch (this)
		{
		case Down:
			return Up;
		case East:
			return West;
		case North:
			return South;
		case NorthEast:
			return SouthWest;
		case NorthWest:
			return SouthEast;
		case South:
			return North;
		case SouthEast:
			return NorthWest;
		case SouthWest:
			return NorthEast;
		case Up:
			return Down;
		case West:
			return East;
		case None:
			return None;
		default:
			throw new AssertionError("Enum instance added without updating reverse: "
					+ this.toString());
		}
	}
}
